#include "NavigationView.h"

#include "OldGraphicsView/Scene.h"

#include <QMouseEvent>
#include <QDebug>

namespace LeGraphicsView
{

    const qreal NavigationView::GrabDistance = 10.0;

    NavigationView::NavigationView(QWidget *parent):
        View(parent),
        m_mainView(nullptr),
        m_move(NoMove)
    {
        setGridEnabled(false);
        setRulerEnabled(false);
        setMouseCursor(LeGraphicsView::View::NoMouseCursor);
        setOriginMark(LeGraphicsView::View::NoOriginMark);

        setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        setAlignment(Qt::AlignCenter);
        setInteractive(false);
        setMouseTracking(true);
    }

    NavigationView::~NavigationView()
    {
    }

    void NavigationView::setMainView(View *mainView)
    {
        if (m_mainView == mainView)
        {
            return;
        }

        if (m_mainView != nullptr)
        {
            m_mainView->viewport()->removeEventFilter(this);
            m_mainView->disconnect(this);
        }

        m_mainView = mainView;

        if (m_mainView != nullptr)
        {
            setScene(m_mainView->scene());
            m_mainView->viewport()->installEventFilter(this);
            connect(m_mainView, &View::settingsChanged,
                    this, &NavigationView::synchroniseSettings);
            synchroniseSettings();
        }
    }

    View *NavigationView::mainView() const
    {
        return m_mainView;
    }

    void NavigationView::synchroniseSettings()
    {
        setPalette(m_mainView->palette());
        setHardwareAccelerationEnabled(m_mainView->hardwareAccelerationEnabled());
    }

    bool NavigationView::canGrabPoint(const QPointF &pos)
    {
        return (mapFromScene(pos) - mapFromGlobal(QCursor::pos())).manhattanLength() < GrabDistance;
    }

    bool NavigationView::canGrabRect(const QRectF &rect)
    {
        return mapFromScene(rect).boundingRect().contains(mapFromGlobal(QCursor::pos()));
    }

    void NavigationView::drawForeground(QPainter *painter, const QRectF &rect)
    {
        View::drawForeground(painter, rect);
        painter->setBrush(Qt::NoBrush);
        painter->setPen(QPen(palette().primaryContent(), 3.0 / transform().m11()));
        painter->drawRect(m_visibleSceneRect);
    }

    void NavigationView::mousePressEvent(QMouseEvent *event)
    {
        if (event->button() != Qt::LeftButton)
        {
            return;
        }

        m_moveOrigin = QCursor::pos();

        if (canGrabPoint(m_visibleSceneRect.topLeft()))
        {
            m_move = MoveTopLeft;
        }
        else if (canGrabPoint(m_visibleSceneRect.topRight()))
        {
            m_move = MoveTopRight;
        }
        else if (canGrabPoint(m_visibleSceneRect.bottomLeft()))
        {
            m_move = MoveBottomLeft;
        }
        else if (canGrabPoint(m_visibleSceneRect.bottomRight()))
        {
            m_move = MoveBottomRight;
        }
        else if (canGrabRect(m_visibleSceneRect))
        {
            m_move = MoveRect;
        }
        else
        {
            m_move = NoMove;
        }
    }

    void NavigationView::mouseReleaseEvent(QMouseEvent *event)
    {
        Q_UNUSED(event);
        m_move = NoMove;
        setCursor(QCursor());
    }

    void NavigationView::mouseMoveEvent(QMouseEvent *event)
    {
        Q_UNUSED(event);

        QPoint pos = mapFromGlobal(QCursor::pos());
        QPointF scenePos = mapToScene(pos);

        switch (m_move)
        {
            case NoMove:
                if (canGrabPoint(m_visibleSceneRect.topLeft()))
                {
                    setCursor(Qt::SizeFDiagCursor);
                }
                else if (canGrabPoint(m_visibleSceneRect.topRight()))
                {
                    setCursor(Qt::SizeBDiagCursor);
                }
                else if (canGrabPoint(m_visibleSceneRect.bottomLeft()))
                {
                    setCursor(Qt::SizeBDiagCursor);
                }
                else if (canGrabPoint(m_visibleSceneRect.bottomRight()))
                {
                    setCursor(Qt::SizeFDiagCursor);
                }
                else if (canGrabRect(m_visibleSceneRect))
                {
                    setCursor(Qt::SizeAllCursor);
                }
                else
                {
                    setCursor(QCursor());
                }
                return;

            case MoveTopLeft:
            {
                QPointF delta = m_visibleSceneRect.topLeft() - scenePos;
                QRectF rect = m_visibleSceneRect.adjusted(-delta.x()/2.0, -delta.y()/2.0,
                                                          delta.x()/2.0, delta.y()/2.0);
                m_mainView->setVisibleSceneRect(rect);
                break;
            }
            case MoveTopRight:
            {
                QPointF delta = m_visibleSceneRect.topRight() - scenePos;
                QRectF rect = m_visibleSceneRect.adjusted(delta.x()/2.0, -delta.y()/2.0,
                                                          -delta.x()/2.0, delta.y()/2.0);
                m_mainView->setVisibleSceneRect(rect);
                break;
            }
            case MoveBottomLeft:
            {
                QPointF delta = m_visibleSceneRect.bottomLeft() - scenePos;
                QRectF rect = m_visibleSceneRect.adjusted(-delta.x()/2.0, delta.y()/2.0,
                                                          delta.x()/2.0, -delta.y()/2.0);
                m_mainView->setVisibleSceneRect(rect);
                break;
            }
            case MoveBottomRight:
            {
                QPointF delta = m_visibleSceneRect.bottomRight() - scenePos;
                QRectF rect = m_visibleSceneRect.adjusted(delta.x()/2.0, delta.y()/2.0,
                                                          -delta.x()/2.0, -delta.y()/2.0);
                m_mainView->setVisibleSceneRect(rect);
                break;
            }
            case MoveRect:
                m_mainView->centerOn(scenePos);
                break;
        }
    }

    bool NavigationView::eventFilter(QObject *watched, QEvent *event)
    {
        m_visibleSceneRect = m_mainView->visibleSceneRect();
        updateForeground();
        return View::eventFilter(watched, event);
    }


    void NavigationView::resizeEvent(QResizeEvent *event)
    {
        Q_UNUSED(event);

        if (scene() != nullptr)
        {
            fitInView(scene()->sceneRect().adjusted(-50, -50, 50, 50),
                      Qt::KeepAspectRatio);
        }
    }

}
